#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int tobit(lua_State *L)
{
    int args_num = lua_gettop(L);
    if (args_num < 1 || args_num % 6) return 0;
    int len = sizeof(int) * args_num;
    int *buff = (int*)malloc(len);
    if (!buff) return 0;
    int i;
    for (i = 0; i < args_num; i += 6)
    {
    *(uint32_t*)(buff + i) = (uint32_t)luaL_checknumber(L, i + 1);
    buff[i + 1] = (int)luaL_checknumber(L, i + 2);
    buff[i + 2] = (int)luaL_checknumber(L, i + 3);
    buff[i + 3] = (int)luaL_checknumber(L, i + 4);
    *(uint32_t*)(buff + i + 4) = (uint32_t)luaL_checknumber(L, i + 5);
    *(uint32_t*)(buff + i + 5) = (uint32_t)luaL_checknumber(L, i + 6);
    }
    lua_pushlstring(L, (char*)buff, len);
    free(buff);
    buff = NULL;
    return 1;
}

static int tohex(lua_State *L)  
{  
    size_t len;
    const char *source = luaL_checklstring(L, 1, &len);
    char *dest = (char*)malloc(len * 2); 
    if (!dest)
    {   
        return 0;
    }   
    short i;  
    unsigned char highByte, lowByte;  

    for (i = 0; i < len; i++)  
    {   
        highByte = (source[i] >> 4) & 0x0f;  
        lowByte = source[i] & 0x0f ;   
        highByte += 0x30;  

        if (highByte > 0x39)  
            dest[i * 2] = highByte + 39;  
        else  
            dest[i * 2] = highByte;  

        lowByte += 0x30;  
        if (lowByte > 0x39)  
            dest[i * 2 + 1] = lowByte + 39;  
        else  
            dest[i * 2 + 1] = lowByte;  
    }   
    lua_pushlstring(L, dest, len * 2); 
    free(dest);
    return 1;
} 


static int tobit2(lua_State *L)
{
    int args_num = lua_gettop(L);
    if (args_num % 2 > 0) return 0;
    int len = 4 * args_num;
    char *buff = (char*)malloc(len);
    if (!buff) return 0;
    char *p = buff;
    int i;
    for (i = 0; i < args_num;)
    {
        switch((int)luaL_checknumber(L, ++i))
        {
            case 0://int32_t
            {
                *(int32_t*)p = (int32_t)luaL_checknumber(L, ++i);
                p += sizeof(int);
                break;
            }
            case 1://int16_t
            {
                *(int16_t*)p = (int16_t)luaL_checknumber(L, ++i);
                p += sizeof(short);
                break;
            }
            case 2://float
            {
                *(float*)p = (float)luaL_checknumber(L, ++i);
                p += sizeof(float);
                break;
            }
            case 4://uint32_t
            {
                *(uint32_t*)p = (uint32_t)luaL_checknumber(L, ++i);
                p += sizeof(uint32_t);
                break;
            }
            case 5://uint16_t
            {
                *(uint16_t*)p = (uint16_t)luaL_checknumber(L, ++i);
                p += sizeof(uint16_t);
                break;
            }
            case 6://uint8_t
            {
                *(uint8_t*)p = (uint8_t)luaL_checknumber(L, ++i);
                p += sizeof(uint8_t);
                break;
            }
            case 7://int8_t
            {
                *(int8_t*)p = (int8_t)luaL_checknumber(L, ++i);
                p += sizeof(int8_t);
                break;
            }
            default:
            {
                break;
            }
        }
    }
    lua_pushlstring(L, (char*)buff, p - buff);
    free(buff);
    buff = NULL;
    return 1;
}

static int sizeofInt(lua_State *L)
{
    switch((int)luaL_checknumber(L, 1))
    {
        case 0://int32_t
        {
            lua_pushnumber(L, sizeof(int));
            break;
        }
        case 1://int16_t
        {
            lua_pushnumber(L, sizeof(short));
            break;
        }
        case 2://float
        {
            lua_pushnumber(L, sizeof(float));
            break;
        }
        case 4://uint32_t
        {
            lua_pushnumber(L, sizeof(uint32_t));
            break;
        }
        case 5://uint16_t
        {
            lua_pushnumber(L, sizeof(uint16_t));
            break;
        }
        case 6://uint8_t
        {
            lua_pushnumber(L, sizeof(uint8_t));
            break;
        }
        case 7://int8_t
        {
            lua_pushnumber(L, sizeof(int8_t));
            break;
        }
        default:
        {
            break;
        }
    }
    return 1;
}

static int toUInt16(lua_State *L)
{
    const char *p = luaL_checkstring(L, 1);
    lua_pushnumber(L, *(uint16_t*)p);
    return 1;
}

static int toUInt32(lua_State *L)
{
    const char *p = luaL_checkstring(L, 1);
    lua_pushnumber(L, *(uint32_t*)p);
    return 1;
}

static int readBuff(lua_State *L)
{
    int args_num = lua_gettop(L);
    if (args_num != 3) return 0;

    size_t len;
    const char *p_str = luaL_checklstring(L, 2, &len);
    
    int st = (int)luaL_checknumber(L, 3);
    p_str += st;
    switch((int)luaL_checknumber(L, 1))
    {
        case 0://int32_t
        {
            lua_pushnumber(L, *(int32_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 1://int16_t
        {
            lua_pushnumber(L, *(int16_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 2://float
        {
            lua_pushnumber(L, *(float*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 4://uint32_t
        {
            lua_pushnumber(L, *(uint32_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 5://uint16_t
        {
            lua_pushnumber(L, *(uint16_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 6://uint8_t
        {
            lua_pushnumber(L, *(uint8_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        case 7://int8_t
        {
            lua_pushnumber(L, *(int8_t*)p_str);
            lua_pushnumber(L, len);
            break;
        }
        default:
        {
            lua_pushlstring(L, p_str, len);
            lua_pushnumber(L, len);
            break;
        }
    }
    
    return 2;
}

static int findRange(lua_State *L)
{
    size_t len;
    const char *p = luaL_checklstring(L, 1, &len);
    int unit_size = (int)luaL_checknumber(L, 2);
    int st = (int)luaL_checknumber(L, 3);
    int ed = (int)luaL_checknumber(L, 4);
    char *ret = (char*)malloc(len);
    if (!ret)
    {
        return 0;
    }
    char *q = (char*)(p + len), *s = ret;
    int val = 0;
    while (p < q)
    {
        val = *(int*)p;
        if (val >= st && val <= ed)
        {
            memcpy(s, p, unit_size);
            s += unit_size;
        }
        p += unit_size;
    }

    lua_pushlstring(L, ret, s - ret);
    free(ret);
    return 1;
}

int binary_search(const char *a, int n, int key, size_t s, int is_front);

static int findRange2(lua_State *L)
{
    size_t len;
    const char *p = luaL_checklstring(L, 1, &len);
    int unit_size = (int)luaL_checknumber(L, 2);
    int st = (int)luaL_checknumber(L, 3);
    int ed = (int)luaL_checknumber(L, 4);
    int *i = (int*)p;
    if (*i >= st)
    {
        st = 0;
    }
    else
    {
        st = binary_search(p, len / unit_size, st, unit_size, -1);
        if (st < 0)
        {
            return 0;
        }
    }
    i = (int*)(p + len - unit_size);
    if (*i <= ed)
    {
        ed = len;
    }
    else
    {
        ed = binary_search(p, len / unit_size, ed, unit_size, 1);
        if (ed < 0)
        {
            return 0;
        }
        else
        {
            ed += unit_size;
        }
    }
    if (st > ed)
    {
        return 0;
    }
    lua_pushnumber(L, st);
    lua_pushnumber(L, ed);
    return 2;
}

static int filter(lua_State *L)
{
    size_t len;
    const char *p = luaL_checklstring(L, 1, &len);
    int unit_size = (int)luaL_checknumber(L, 2);
    char *ret = (char*)malloc(len);
    if (!ret)
    {
        return 0;
    }
    char *q = (char*)(p + len), *s = ret;
    int val = 0;
    while (p < q)
    {
        if (*(int*)p > val)
        {
            memcpy(s, p, unit_size);
            val = *(int*)p;
            s += unit_size;
        }
        p += unit_size;
    }

    lua_pushlstring(L, ret, s - ret);
    free(ret);
    return 1;
}

static const struct luaL_Reg xbit[] = {
    {"tobit", tobit},
    {"tobit2", tobit2},
    {"toUInt16", toUInt16},
    {"findRange", findRange},
    {"findRange2", findRange2},
    {"filter", filter},
    {"readBuff", readBuff},
    {"sizeofInt", sizeofInt},
    {"toUInt32", toUInt32},
    {"tohex", tohex},
    {NULL, NULL},
};

int luaopen_xbit(lua_State *L)
{
    luaL_register(L, "xbit", xbit);
    return 1;
}